<template>
  <div class="role">
    <div class="btns">
      <el-button type="primary" @click="dialogVisible = true">添加</el-button>
      <!-- <el-button type="primary" @click="drawerVisible = true">打开抽屉</el-button> -->
    </div>

    <!-- 表格 -->
    <el-table
      :data="roles"
      style="width: 100%; margin-bottom: 20px"
      row-key="id"
      border
    >
      <template #empty>
        <el-empty description="暂无数据" />
      </template>
      <el-table-column type="index" label="#" width="50" />
      <el-table-column prop="name" label="角色名称" />
      <el-table-column prop="description" label="角色描述" />
      <el-table-column prop="icon" label="操作">
        <template #default="{row}">
          <el-button type="primary" size="small" @click="handleRevoke(row)">授权</el-button>
          <el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
          <el-button type="danger" size="small" @click="handleRemove(row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 添加与修改的对话框 -->
    <el-dialog v-model="dialogVisible" :title="(roleItem.id ? '修改' : '添加') + '角色'" width="500">
      <el-form :model="roleItem" label-width="50px" label-suffix=":">
        <el-form-item label="名称">
          <el-input v-model="roleItem.name" placeholder="请输入角色名称" />
        </el-form-item>
        <el-form-item label="描述">
          <el-input
            type="textarea"
            :rows="4"
            v-model="roleItem.description"
            placeholder="请输入角色描述信息"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary" @click="handleSave">
            {{ roleItem.id ? '修改' : '添加' }}
          </el-button>
        </div>
      </template>
    </el-dialog>

    <!-- 授权的抽屉效果 -->
    <el-drawer
      v-model="drawerVisible"
      title="角色授权"
      direction="rtl"
    >
      <div class="demo-drawer__content">
        <!-- 树形控件，用于显示菜单权限列表 -->
        <el-tree
          style="max-width: 600px"
          show-checkbox
          :data="menus"
          node-key="id"
          :props="{ label: 'name' }"
          ref="menuRef"
          default-expand-all
        />
        <div style="text-align: right;">
          <el-button type="danger" size="small" @click="handleCheckAll">全选</el-button>
          <el-button size="small" @click="drawerVisible = false">取消</el-button>
          <el-button type="primary" size="small" @click="handleSavePermission">保存</el-button>
        </div>
      </div>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import { ElMessageBox } from 'element-plus'
import { getAllRoles, addRole, updateRole, deleteRole } from '@/api/role'
import { getAllMenuPermissions } from '@/api/menu-permission'
import { transToTree, getLeafNodeKeys } from '@/utils'

// 对话框显隐状态
const dialogVisible = ref<boolean>(false)
// 抽屉显隐状态
const drawerVisible = ref<boolean>(false)

// 所有角色数据
const roles = ref<IRole[]>([])

/**
 * 查询所有角色数据
 */
const loadRoles = async () => {
  const res = await getAllRoles()
  roles.value = res.list
}
loadRoles()

// 表单中的角色数据
const roleItem = ref<IRole>({
  id: '',
  name: '',
  description: '',
})

// 监视对话框关闭时，重置表单中的数据
watch(dialogVisible, (newValue) => {
  if (!newValue) {
    roleItem.value = {
      id: '',
      name: '',
      description: '',
    }
  }
})

/**
 * 点击编辑按钮，打开对话框，回显表单数据
 */
const handleEdit = (row: IRole) => {
  // 显示对话框
  dialogVisible.value = true
  // 回显表单数据，将待修改的行数据进行复制后处理
  roleItem.value = { ...row }
}

/**
 * 保存表单中提交的角色信息: 添加或修改
 */
const handleSave = async() => {
  // 添加
  if (!roleItem.value.id) {
    await addRole(roleItem.value)
  } else {
    // 修改
    await updateRole(roleItem.value)
  }

  // 添加成功
  dialogVisible.value = false
  // 重新加载角色列表
  loadRoles()
}

/**
 * 删除角色，需要进行二次确认
 */
const handleRemove = async(row: IRole) => {
  try {
    const result = await ElMessageBox.confirm('确定要删除该角色吗？', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    })
    if (result === 'confirm') {
      // 删除
      await deleteRole(row.id)
      // 重新加载角色列表
      loadRoles()
    }
  } catch (error) {
    console.log('error:', error)
  }
}

// 所有菜单权限数据
const menus = ref<IPermission[]>([])
// 菜单权限树形控件的引用
const menuRef = ref<any>()
// 待授权的角色数据
const revokeRole = ref<IRole>()

/**
 * 加载所有的菜单权限数据
 */
const loadMenus = async () => {
  // 查询所有菜单权限数据
  const res = await getAllMenuPermissions()
  // 将菜单权限数据转换为树形结构
  menus.value = transToTree(res.list)

  console.log('menus:', menus.value)
}
/**
 * 点击授权按钮，显示授权抽屉
 */
const handleRevoke = async(row: IRole) => {
  drawerVisible.value = true
  // 设置待授权的角色信息
  revokeRole.value = { ...row }
  // 判断是否有菜单权限数据，没有则加载
  if (menus.value.length === 0) {
    await loadMenus()
  }

  // 获取当前待授权角色的已有权限列表，并勾选
  console.log('row:', row)
  // row.permissions 中既有半选状态的权限，也有全选状态的权限
  // 半选状态的权限，需要单独处理，不能直接调用 setCheckedKeys 处理
  // menuRef.value.setCheckedKeys(row.permissions)
  // 需要找出 row.permissions 数组中所有的叶子节点权限 id
  const leafKeys = getLeafNodeKeys(menus.value, row.permissions as string[])
  menuRef.value.setCheckedKeys(leafKeys)
}

/**
 * 全选所有权限
 */
const handleCheckAll = () => {
  menuRef.value.setCheckedNodes(menus.value)
}

/**
 * 保存角色授权数据
 */
const handleSavePermission = async () => {
  // 获取在树形控件中勾选与半选的节点数据
  const checkedKeys = menuRef.value.getCheckedKeys()
  console.log('checkedKeys:', checkedKeys)
  const halfCheckedKeys = menuRef.value.getHalfCheckedKeys()
  console.log('halfCheckedKeys:', halfCheckedKeys)
  // 合并权限的列表 keys
  const permissions = [...checkedKeys, ...halfCheckedKeys]
  if (revokeRole.value) {
    revokeRole.value.permissions = permissions
    // 调用 api 方法，实现角色数据修改
    await updateRole(revokeRole.value)
    // 重新加载角色列表
    loadRoles()
  }
  // 关闭抽屉
  drawerVisible.value = false
}
</script>

<style lang="scss" scoped>
.role {
  padding: 12px;

  .btns {
    margin-bottom: 12px;
  }
}
</style>
